#ifndef SM2_FP_H
#define SM2_FP_H

#include "common.h"

// Definition of types *************************************************************************************************
/**
 * @typedef sm2_fp
 * @brief Defines the type representing a 256-bit number in finite prime field.
 * @typedef sm2_bn
 * @brief Defines the type representing a 32-byte array for finite field arithmetic.
 * @typedef sm2_dfp
 * @brief Defines the type representing a 512-bit number for double precision finite field arithmetic.
 * @typedef sm2_naf
 * @brief Defines the type representing a Non-Adjacent Form (NAF) for efficient scalar multiplication
 */
typedef u32  sm2_fp[8];
typedef u8   sm2_bn[32];
typedef u32  sm2_dfp[16];
typedef i8   naf[257];

// Declarations of constant values *************************************************************************************

extern const sm2_fp sm2_zero;   // Constant representing zero
extern const sm2_fp sm2_one;    // Constant representing one
extern const sm2_fp sm2_p;      // Constant representing the prime p for SM2
extern const sm2_fp sm2_n;      // Constant representing the order n for SM2
extern const sm2_fp sm2_a;      // Constant representing the curve parameter a for SM2
extern const sm2_fp sm2_b;      // Constant representing the curve parameter b for SM2
extern const sm2_fp sm2_s;      // Constant representing (sm2_p + 1) / 2

// Declarations of functions *******************************************************************************************
/**
 * @brief Sets the value of one number to another.
 * @param r The result number.
 * @param a The number to copy from.
 */
void sm2_fp_set(sm2_fp r, const sm2_fp a);

/**
 * @brief Checks if the given number is odd.
 * @param a The number to check.
 * @return 1 if odd, 0 otherwise.
 */
int sm2_fp_is_odd(const sm2_fp a);

/**
 * @brief Checks if the given number is even.
 * @param a The number to check.
 * @return 1 if even, 0 otherwise.
 */
int sm2_fp_is_even(const sm2_fp a);

/**
 * @brief Checks if the given number is zero.
 * @param a The number to check.
 * @return 1 if zero, 0 otherwise.
 */
int sm2_fp_is_zero(const sm2_fp a);

/**
 * @brief Checks if the given number is not zero.
 * @param a The number to check.
 * @return 1 if not zero, 0 otherwise.
 */
int sm2_fp_non_zero(const sm2_fp a);

/**
 * @brief Compares two numbers for equality.
 * @param a The first number.
 * @param b The second number.
 * @return 1 if equal, 0 otherwise.
 */
int sm2_fp_equ(const sm2_fp a, const sm2_fp b);

/**
 * @brief Compares two numbers.
 * @param a The first number.
 * @param b The second number.
 * @return Negative if a < b, positive if a > b, zero if equal.
 */
int sm2_fp_cmp(const sm2_fp a, const sm2_fp b);


/**
 * @brief Generates a random number in the range [0, p).
 * @param r The result number.
 * @param p The upper bound.
 */
void sm2_fp_rand(sm2_fp r, const sm2_fp p);

/**
 * @brief Adds two numbers.
 * @param r The result number.
 * @param a The first number.
 * @param b The second number.
 */
void sm2_fp_add(sm2_fp r, const sm2_fp a, const sm2_fp b);

/**
 * @brief Subtracts one number from another.
 * @param r The result number.
 * @param a The minuend.
 * @param b The subtrahend.
 */
void sm2_fp_sub(sm2_fp r, const sm2_fp a, const sm2_fp b);

/**
 * @brief Negates a number.
 * @param r The result number.
 * @param a The number to negate.
 */
void sm2_fp_neg(sm2_fp r, const sm2_fp a);

/**
 * @brief Half of a number.
 * @param r The result number.
 * @param a The number to halve.
 */
void sm2_fp_haf(sm2_fp r, const sm2_fp a);

/**
 * @brief Doubles a number.
 * @param r The result number.
 * @param a The number to double.
 */
void sm2_fp_dbl(sm2_fp r, const sm2_fp a);

/**
 * @brief Triples a number.
 * @param r The result number.
 * @param a The number to triple.
 */
void sm2_fp_tri(sm2_fp r, const sm2_fp a);

/**
 * @brief Recovers a number from a dnumber.
 * @param r The result number.
 * @param c The dnumber to recover from.
 */
void sm2_fp_rec(sm2_fp r, const sm2_dfp c);

/**
 * @brief Multiplies two numbers.
 * @param r The result number.
 * @param a The first number.
 * @param b The second number.
 */
void sm2_fp_mul(sm2_fp r, const sm2_fp a, const sm2_fp b);

/**
 * @brief Squares a number.
 * @param r The result number.
 * @param a The number to square.
 */

void sm2_fp_sqr(sm2_fp r, const sm2_fp a);

/**
 * @brief Cubes a number.
 * @param r The result number.
 * @param a The number to cube.
 */
void sm2_fp_cub(sm2_fp r, const sm2_fp a);

/**
 * @brief Computes the modular inverse of a number.
 * @param r The result number.
 * @param q The number to invert.
 */
void sm2_fp_inv(sm2_fp r, const sm2_fp q);

/**
 * @brief Adds two numbers in a finite field.
 * @param r The result number.
 * @param a The first number.
 * @param b The second number.
 */
void sm2_fn_add(sm2_fp r, const sm2_fp a, const sm2_fp b);

/**
 * @brief Subtracts one number from another in a finite field.
 * @param r The result number.
 * @param a The minuend.
 * @param b The subtrahend.
 */
void sm2_fn_sub(sm2_fp r, const sm2_fp a, const sm2_fp b);

/**
 * @brief Recovers a number from a dnumber in a finite field.
 * @param r The result number.
 * @param a The dnumber to recover from.
 */
void sm2_fn_rec(sm2_fp r, const sm2_dfp a);

/**
 * @brief Multiplies two numbers in a finite field.
 * @param r The result number.
 * @param a The first number.
 * @param b The second number.
 */
void sm2_fn_mul(sm2_fp r, const sm2_fp a, const sm2_fp b);

/**
 * @brief Computes the modular inverse of a number in a finite field.
 * @param r The result number.
 * @param q The number to invert.
 */
void sm2_fn_inv(sm2_fp r, const sm2_fp q);

/**
 * @brief Converts a number to a string representation.
 * @param n The number to convert.
 * @param s The string buffer to store the result.
 */
void sm2_fp_to_str(const sm2_fp n, char* s);

/**
 * @brief Fills a number from a string representation.
 * @param n The result number.
 * @param s The string to convert from.
 */
void sm2_fp_from_str(sm2_fp n, const char* s);

/**
 * @brief Converts a number to a byte array.
 * @param n The number to convert.
 * @param b The byte array to store the result.
 */
void sm2_fp_to_bn(const sm2_fp n, sm2_bn b);

/**
 * @brief Fills a number from a byte array.
 * @param b The byte array to convert from.
 * @param n The result number.
 */
void sm2_bn_to_fp(const sm2_bn b, sm2_fp n);

/**
 * @brief Converts a byte array to a string representation.
 * @param b The byte array to convert.
 * @param s The string buffer to store the result.
 */
void sm2_bn_to_str(const sm2_bn b, char* s);

/**
 * @brief Fills a byte array from a string representation.
 * @param b The result byte array.
 * @param s The string to convert from.
 */
void sm2_bn_from_str(sm2_bn b, const char* s);

/**
 * @brief Computes the Non-Adjacent Form (NAF) representation of a number.
 * @param r The result NAF array.
 * @param w The width of NAF.
 * @param n The number to compute the NAF for.
 */
void sm2_fp_naf(naf r, const u8 w, const sm2_fp n);

/**
 * @brief Computes the Non-Adjacent Form (NAF) representation of a number and only used in sm2_jpoint_gen.
 * @param K The result NAF array.
 * @param k The number to compute the NAF for.
 */
void sm2_fp_nafp(i8 K[52], const sm2_fp k);

#endif //SM2_FP_H